package com.java2e.martin.erd.service.sql;

import cn.fisok.pdman.dbreverse.Entity;
import cn.fisok.pdman.dbreverse.Field;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLName;
import com.alibaba.druid.sql.ast.statement.*;
import com.alibaba.druid.sql.dialect.mysql.ast.MySqlPrimaryKey;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OraclePrimaryKey;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author xiaobai
 */
public abstract class AbstractStatementParser implements StatementParser {

    /**
     *
     */
    private static final String NOT_NULL = "NOT_NULL";

    @Override
    public Entity buildEntity(SQLCreateTableStatement createTableStatement) {
        String name = extractName(createTableStatement.getName()).toUpperCase();
        String comment = extractComment(createTableStatement.getComment());
        Entity entity = new Entity();
        entity.setTitle(name);
        entity.setChnname(comment);
        entity.setFields(buildFields(createTableStatement.getTableElementList()));
        return entity;
    }

    /**
     * buildFields
     *
     * @param elements elements
     * @return List<Field>
     */
    public static List<Field> buildFields(List<SQLTableElement> elements) {
        List<Field> fields = new ArrayList<>(20);
        Map<String, Field> fieldMap = new HashMap<>(20);
        for (SQLTableElement element : elements) {
            // 字段
            if (element instanceof SQLColumnDefinition) {
                SQLColumnDefinition sqlColumnDefinition = (SQLColumnDefinition) element;
                // 字段名称
                String columnName = extractName(sqlColumnDefinition.getName()).toUpperCase();
                // 字段备注
                String columnComment = extractComment(sqlColumnDefinition.getComment());
                // 字段类型
                String dataType = sqlColumnDefinition.getDataType().getName().toUpperCase();
                // 是否自增
                boolean autoIncrement = sqlColumnDefinition.isAutoIncrement();
                // 遍历约束
                Map<String, Boolean> constraintMap = traverseConstraints(sqlColumnDefinition.getConstraints());
                // 是否允许为空
                boolean notNull = constraintMap.get(NOT_NULL) != null;

                Field field = new Field();
                field.setName(columnName);
                field.setChnname(columnComment);
                field.setType(dataType);
                field.setAutoIncrement(autoIncrement);
                field.setNotNull(notNull);
                field.setPk(false);
                fields.add(field);
                fieldMap.put(columnName, field);
            }

            // Mysql主键, 暂时先不支持联合主键
            if (element instanceof MySqlPrimaryKey) {
                MySqlPrimaryKey mysqlPrimaryKey = (MySqlPrimaryKey) element;
                String columnName = extractExpr(mysqlPrimaryKey.getColumns().get(0).getExpr()).toUpperCase();
                fieldMap.get(columnName).setPk(true);
            }

            // Oracle主键, 暂时先不支持联合主键
            if (element instanceof OraclePrimaryKey) {
                OraclePrimaryKey oraclePrimaryKey = (OraclePrimaryKey) element;
                String columnName = extractExpr(oraclePrimaryKey.getColumns().get(0).getExpr()).toUpperCase();
                fieldMap.get(columnName).setPk(true);
            }
        }

        return fields;
    }

    /**
     * traverseConstraints
     *
     * @param constraints constraints
     * @return Map<String, Boolean>
     */
    private static Map<String, Boolean> traverseConstraints(List<SQLColumnConstraint> constraints) {
        Map<String, Boolean> map = new HashMap<>(20);
        constraints.forEach(constraint -> {
            // 非空约束
            if (constraint instanceof SQLNotNullConstraint) {
                map.put(NOT_NULL, true);
            }
        });
        return map;
    }

    /**
     * extractComment
     *
     * @param comment comment
     * @return String
     */
    public static String extractComment(SQLExpr comment) {
        if (comment == null) {
            return "";
        }

        return comment.toString().trim()
                .replace("'", "")
                .replace("\"", "");
    }

    /**
     * extractName
     *
     * @param name name
     * @return String
     */
    public static String extractName(SQLName name) {
        return name.getSimpleName().trim().replace("'", "")
                .replace("`", "")
                .replace("\"", "");
    }

    /**
     * extractExpr
     *
     * @param expr expr
     * @return String
     */
    public static String extractExpr(SQLExpr expr) {
        return expr.toString().trim()
                .replace("'", "")
                .replace("`", "")
                .replace("\"", "");
    }

}
